OSI七层协议模型
OSI与TCP/IP四层协议的关系
TCP/IP四层协议
网络层:IP
传输层:TCP UDP
应用层:HTTP SSH
IP地址是Internet协议的一部分,用于在计算机网络中唯一地标识主机。
IP地址结构
【IPv4】32位无符号整数 ;【IPv6】128位无符号整数
网络字节顺序——大端字节顺序
IP地址结构中存放的数据总是以大端法存放的,即使主机字节顺序是小端法
IP地址的转换
“n” stands for network,the “p” stands for presentation.
网络字节顺序转换
点分十进制转换
IP地址的分类
类型分类:IPv4,IPv6
地址范围分类
公网私网分类
私网IP
A类地址:10.0.0.0——10.255.255.255
B类地址:172.16.0.0——172.31.255.255
C类地址:192.168.0.0——192.168.255.255
公网地址和私网地址的区别
分配方式不同:公网地址由互联网注册机构分配,而私网地址由局域网管理员自行分配。
使用范围不同:公网地址用于Internet上的设备,私网地址用于局域网内部的设备。
唯一性和可达性不同:公网地址具有全球唯一性和全球可达性,而私网地址只在局域网内部唯一,不具有全球可达性。
转发方式不同:公网地址可以直接访问Internet上的其他设备,而私网地址需要通过路由器进行转发才能访问Internet上的其他设设备。
私网的实现:子网掩码 计算机网络 IP地址与子网划分
私网的Internet接入:NAT技术——将私网地址映射成(某一网关)公网地址,并接入Internet
IP地址和MAC地址的区别
IP地址是服务商给你的,mac地址是你的网卡物理地址;
IP地址局域网内可以随便更改,但是mac地址一般不能更改;
长度不同, IP地址为32位,MAC地址为48位;
寻址协议层不同, IP地址应用于OSI第三层,即网络层,而MAC地址应用在OSI第二层,即数据链路层。
域名集合
从节点反向到根的路径形成域名
一级域名由ICANN定义,二级域名由ICANN的授权代理分配,有二级域名的组织可以在子域中创造新域名
DNS数据库
DNS数据库维护域名集合和IP地址之间的映射多对多
本地定义的域名localhost-回送地址127.0.0.1
实际域名Linux>hostname
xxxxxxxxxx11linux> nslookup <name>TCP:点对点,全双工,可靠的
套接字:套接字是连接的一个端点 IPaddr:port主机+进程
套接字对:(cliaddr:cliport, servaddr:servport)
套接字
从Linux内核来看,一个套接字就是连接的一个端点
从Linux程序来看,套接字就是一个有相应描述符的打开文件
套接字对:一个连接是由它两端的套接字地址唯一确定的,这对套接字地址叫做套接字对
元组表示(cliaddr:cliport,servaddr:servport)
套接字接口:一组函数,和Unix I/O结合起来创建网络应用
套接字地址:IPaddr:port,由一个因特网地址和一个16位整数端口组成
套接字地址结构:因特网的套接字地址存放在如下16字节结构体struct sockaddr_in中
x1/* IP socket address structure */2struct sockaddr_in {3 uint16_t sin_family; /* Protocol family (always AF_INET) */4 uint16_t sin_port; /* Port number in network byte order */5 struct in_addr sin_addr; /* IP address in network byte order */6 unsigned char sin_zero[8]; /* Pad to sizeof(struct sockaddr) */7};89/* Generic socket address structure (for connect, bind, and accept) */10struct sockaddr {11 uint16_t sa_family; /* Protocol family */12 char sa_data[14]; /* Address data */13};sin_family:协议族
sin_port:端口号
sin_addr:IP 地址
sin_zero[8]:空白
getaddrinfo函数将主机名、主机地址、服务名和端口号的字符串表示转化成套接字地址结构
xxxxxxxxxx81int getaddrinfo(const char *host, /* Hostname or address */2 const char *service, /* Port or service name */3 const struct addrinfo *hints,/* Input parameters */4 struct addrinfo **result); /* Output linked list */56void freeaddrinfo(struct addrinfo *result); /* Free linked list */78const char *gai_strerror(int errcode); /* Return error msg */host参数
host 和 service 其中之一可以为 NULL
service参数
host 和 service 其中之一可以为 NULL
hints参数
只能设置下列字段:ai_family、ai_socktype、ai_protocol 和ai_flags 字段。其他字段必须设置为 0。
ai_family:设置为 AF_INET 会将列表限制为 IPv4 地址;设置为 AF_INET6 则限制为 IPv6 地址。
ai_socktype:设置为 SOCK_STREAM 将列表限制为对每个地址最多一个 addrinfo 结构。
ai_protocol :一般为 0
ai_flags :位掩码,用或运算连接
AI_ADDRCONFIG 表示返回的 IP 地址类型与主机相同
AI_CANONNAME 表示返回的结构中第一个字段的 ai_canoname 指向官方给出的权威域名
AI_NUMERICSERV 强制参数中的服务名为端口号
AI_PASSIVE 表示返回的套接字地址可能作为监听套接字,此时 host 必须为 NULL,得到的套接字中 IP 地址字段为通配符地址(wildcard address)
计算机网络的掩码,如果一个分公司的局域网网段是10.1.0.0/16,需要通过路由协议来通告给公司总部,那如何通告这个网段呢? 10.1.0.0 255.255.0.0(网段掩码)
以上用掩码来完成网段的匹配,那通配符掩码一般用于什么地方呢?用于匹配主机(host)! 如果一个公司规定只有 IP 地址位于 10.1.0.0/16 的主机才可以上网,那如何创建这个ACL呢? 允许 10.1.0.0 0.0.255.255(通配符掩码) 这个ACL的意思是:只要IP地址位于 10.1.0.0 - 10.1.255.255的主机就可以允许上网,否则不允许。
result参数:返回一个指向addrinfo结构的链表
返回值:通过域名(或 IP 地址)host 和服务名(或端口号)service 获得 result 结构,类型为 struct addrinfo
成功返回 0
失败返回非 0,错误代码
getnameinfo函数。用于从套接字地址SA中提取出主机和服务名字符串。
xxxxxxxxxx41int getnameinfo(const struct sockaddr *sa, socklen_t salen, 2 char *host, size_t hostlen, 3 char *service, size_t servlen, int flags);4// Returns: 0 if OK, nonzero error code on errorflags参数:位掩码
NI_NUMBERICHOST: 强制返回数字的地址字符串。
NI_NUMBERICSERV: 强制返回数字的端口号。
客户端和服务器使用 socket 函数来创建一个套接字描述符(socket descriptor)
xxxxxxxxxx11int socket(int domain, int type, int protocol);硬编码的参数来调用 socket 函数 clientfd = Socket(AF_INET, SOCK_STREAM, 0);
创建套接字描述符,成功返回套接字描述符,失败返回 -1
domain 参数
表示地址族,如 IF_INET 表示 IPv4,IF_INET6 表示 IPv6,IF_UNIX 表示本地传输
type 参数
表示传输方式 ,如 SOCK_STREAM 表示流式套接字(TCP 传输),SOCK_DGRAM 表示包式套接字(UDP 传输),SOCK_RAW 表示原始套接字
protocol参数
表示限定协议,一般填 0 表示不限制
xxxxxxxxxx11int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);服务器将 sockfd 绑定到套接字 addr 上, 成功返回 0,失败返回 -1
xxxxxxxxxx11int listen(int sockfd, int backlog);服务器调用 listen 函数告诉内核,描述符是被服务器而不是客户端使用的。
backlog 参数
暗示了内核在开始拒绝连接请求之前,队列中要排队的未完成的连接请求的数量,一般为1024
客户端通过调用 connect 函数来建立和服务器的连接
xxxxxxxxxx11int connect(int clientfd, const struct sockaddr *addr, socklen_t addrlen);客户端将 clientfd 绑定到套接字 addr 上,成功返回 0,失败返回 -1
connect 函数会阻塞,一直到连接成功建立或是发生错误
xxxxxxxxxx11int accept(int listenfd, struct sockaddr *addr, int *addrlen);在监听套接字 listenfd 的排队队列中找到一个 connect 请求的套接字绑定到套接字 addr 上,并创建一个新的连接套接字用于传输,成功返回 0,失败返回 -1
xxxxxxxxxx21int open_clientfd(char *hostname, char *port); //客户端调用,建立与服务器的连接2int open_listenfd(char *port); //服务端调用,创建监听描述符,准备接受连接请求open_clientfd:服务器运行在主机hostname,并在端口号port上监听连接请求
open_listenfd:监听描述符准备在端口port上接受发送到本机(所有IP地址)的连接请求
xxxxxxxxxx301int open_clientfd(char *hostname, char *port) {2 int clientfd;3 struct addrinfo hints, *listp, *p;45 /* Get a list of potential server addresses */6 memset(&hints, 0, sizeof(struct addrinfo));7 hints.ai_socktype = SOCK_STREAM; /* Open a connection */8 hints.ai_flags = AI_NUMERICSERV; /* ... using a numeric port arg. */9 hints.ai_flags |= AI_ADDRCONFIG; /* Recommended for connections */10 Getaddrinfo(hostname, port, &hints, &listp);1112 /* Walk the list for one that we can successfully connect to */13 for (p = listp; p; p = p->ai_next) {14 /* Create a socket descriptor */15 if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)16 continue; /* Socket failed, try the next */1718 /* Connect to the server */19 if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1)20 break; /* Success */21 Close(clientfd); /* Connect failed, try another */22 }2324 /* Clean up */25 Freeaddrinfo(listp);26 if (!p) /* All connects failed */27 return -1;28 else /* The last connect succeeded */29 return clientfd;30}xxxxxxxxxx401int open_listenfd(char *port)2{3 struct addrinfo hints, *listp, *p;4 int listenfd, optval = 1;56 /* Get a list of potential server addresses */7 memset(&hints, 0, sizeof(struct addrinfo));8 hints.ai_socktype = SOCK_STREAM; /* Accept connections */9 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... on any IP address */10 hints.ai_flags |= AI_NUMERICSERV; /* ... using port number */11 Getaddrinfo(NULL, port, &hints, &listp);1213 /* Walk the list for one that we can bind to */14 for (p = listp; p; p = p->ai_next) {15 /* Create a socket descriptor */16 if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)17 continue; /* Socket failed, try the next */1819 /* Eliminates "Address already in use" error from bind */20 Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,21 (const void *)&optval , sizeof(int));2223 /* Bind the descriptor to the address */24 if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)25 break; /* Success */26 Close(listenfd); /* Bind failed, try the next */27 }2829 /* Clean up */30 Freeaddrinfo(listp);31 if (!p) /* No address worked */32 return -1;3334 /* Make it a listening socket ready to accept connection requests */35 if (listen(listenfd, LISTENQ) < 0) {36 Close(listenfd);37 return -1;38 }39 return listenfd;40}xxxxxxxxxx26123int main(int argc, char **argv)4{5 int clientfd;6 char *host, *port, buf[MAXLINE];7 rio_t rio;89 if (argc != 3) {10 fprintf(stderr, "usage: %s <host> <port>\n", argv[0]);11 exit(0);12 }13 host = argv[1];14 port = argv[2];1516 clientfd = Open_clientfd(host, port);17 Rio_readinitb(&rio, clientfd);1819 while (Fgets(buf, MAXLINE, stdin) != NULL) {20 Rio_writen(clientfd, buf, strlen(buf));21 Rio_readlineb(&rio, buf, MAXLINE);22 Fputs(buf, stdout);23 }24 Close(clientfd);25 exit(0);26}xxxxxxxxxx28123void echo(int connfd);45int main(int argc, char **argv)6{7 int listenfd, connfd;8 socklen_t clientlen;9 struct sockaddr_storage clientaddr; /* Enough space for any address */10 char client_hostname[MAXLINE], client_port[MAXLINE];1112 if (argc != 2) {13 fprintf(stderr, "usage: %s <port>\n", argv[0]);14 exit(0);15 }16 17 listenfd = Open_listenfd(argv[1]);18 while (1) {19 clientlen = sizeof(struct sockaddr_storage);20 connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);21 Getnameinfo((SA *) &clientaddr, clientlen, client_hostname, MAXLINE,22 client_port, MAXLINE, 0);23 printf("Connected to (%s, %s)\n", client_hostname, client_port);24 echo(connfd);25 Close(connfd);26 }27 exit(0);28}xxxxxxxxxx121void echo(int connfd)2{3 size_t n;4 char buf[MAXLINE];5 rio_t rio;67 Rio_readinitb(&rio, connfd);8 while ((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {9 printf("server received %d bytes\n", (int)n);10 Rio_writen(connfd, buf, n);11 }12}